package com.xxl.job.core.util;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.registry.client.util.json.BasicJson;

/**
 * @author xuxueli 2018-11-25 00:55:31
 */
public final class XxlJobRemotingUtil {

	private static Logger logger = LoggerFactory.getLogger(XxlJobRemotingUtil.class);
	public static String XXL_RPC_ACCESS_TOKEN = "XXL-RPC-ACCESS-TOKEN";

	// trust-https start
	private static void trustAllHosts(HttpsURLConnection connection) {
		try {
			SSLContext sc = SSLContext.getInstance("TLS");
			sc.init(null, trustAllCerts, new java.security.SecureRandom());
			SSLSocketFactory newFactory = sc.getSocketFactory();
			connection.setSSLSocketFactory(newFactory);
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
		connection.setHostnameVerifier((hostname, session) -> true);
	}

	private static final TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {

		@Override
		public java.security.cert.X509Certificate[] getAcceptedIssuers() {
			return new java.security.cert.X509Certificate[] {};
		}

		@Override
		public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
		}

		@Override
		public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
		}
	} };
	// trust-https end

	/**
	 * post
	 * @param url
	 * @param accessToken
	 * @param requestObj
	 * @return
	 */
	public static ReturnT<String> postBody(String url, String accessToken, Object requestObj, int timeout) {
		HttpURLConnection connection = null;
		BufferedReader bufferedReader = null;
		try {
			// connection
			URL realUrl = new URL(url);
			connection = (HttpURLConnection) realUrl.openConnection();
			// trust-https
			boolean useHttps = url.startsWith("https");
			if (useHttps) {
				HttpsURLConnection https = (HttpsURLConnection) connection;
				trustAllHosts(https);
			}
			// connection setting
			connection.setRequestMethod("POST");
			connection.setDoOutput(true);
			connection.setDoInput(true);
			connection.setUseCaches(false);
			connection.setReadTimeout(timeout * 1000);
			connection.setConnectTimeout(3 * 1000);
			connection.setRequestProperty("connection", "Keep-Alive");
			connection.setRequestProperty("Accept", "application/json");
			connection.setRequestProperty("Charset", "UTF-8");
			connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
			if (accessToken != null && accessToken.trim().length() > 0) {
				connection.setRequestProperty(XXL_RPC_ACCESS_TOKEN, accessToken);
			}
			// do connection
			connection.connect();
			// write requestBody
			String requestBody = BasicJson.toJson(requestObj);
			DataOutputStream dataOutputStream = new DataOutputStream(connection.getOutputStream());
			dataOutputStream.write(requestBody.getBytes("UTF-8"));
			dataOutputStream.flush();
			dataOutputStream.close();
			/*byte[] requestBodyBytes = requestBody.getBytes("UTF-8");
			connection.setRequestProperty("Content-Length", String.valueOf(requestBodyBytes.length));
			OutputStream outwritestream = connection.getOutputStream();
			outwritestream.write(requestBodyBytes);
			outwritestream.flush();
			outwritestream.close();*/
			// valid StatusCode
			int statusCode = connection.getResponseCode();
			if (statusCode != 200) {
				return new ReturnT<>(ReturnT.FAIL_CODE, "xxl-rpc remoting fail, StatusCode(" + statusCode + ") invalid. for url : " + url, "");
			}
			// result
			bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
			StringBuilder result = new StringBuilder();
			String line;
			while ((line = bufferedReader.readLine()) != null) {
				result.append(line);
			}
			String resultJson = result.toString();
			// parse returnT
			try {
				Map<String, Object> resultMap = BasicJson.parseMap(resultJson);
				ReturnT<String> returnT = new ReturnT<>();
				if (resultMap == null) {
					returnT.setCode(ReturnT.FAIL_CODE);
					returnT.setMsg("AdminBizClient Remoting call fail.");
				} else {
					returnT.setCode(Integer.valueOf(String.valueOf(resultMap.get("code"))));
					returnT.setMsg(String.valueOf(resultMap.get("msg")));
					returnT.setContent(String.valueOf(resultMap.get("content")));
				}
				return returnT;
			} catch (Exception e) {
				logger.error("xxl-rpc remoting (url=" + url + ") response content invalid(" + resultJson + ").", e);
				return new ReturnT<>(ReturnT.FAIL_CODE, "xxl-rpc remoting (url=" + url + ") response content invalid(" + resultJson + ").", "");
			}
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			return new ReturnT<>(ReturnT.FAIL_CODE, "xxl-rpc remoting error(" + e.getMessage() + "), for url : " + url, "");
		} finally {
			try {
				if (bufferedReader != null) {
					bufferedReader.close();
				}
				if (connection != null) {
					connection.disconnect();
				}
			} catch (Exception e2) {
				logger.error(e2.getMessage(), e2);
			}
		}
	}
}
